package com.demo.controller;

import com.demo.draw.DrawNewCard;
import com.demo.draw.MovingCard;
import com.demo.model.Board;
import com.demo.model.Card;
import com.demo.view.GameWindow;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

/**
 * 卡片控制器，也是游戏规则
 */
public class CardsHandler {
    /**
     * 单例模式
     */
    private static final CardsHandler instance = new CardsHandler();

    private CardsHandler() {
    }

    /**
     * 将邻接矩阵复制一份
     * <p>
     * 为了不影响原始卡片的邻接矩阵，通过该方法获得一个拷贝，对拷贝进行任何操作都是安全的
     * <p>
     *
     * @param src 复制来源
     * @return 返回复制完成后的一份拷贝
     */
    public static int[][] copyGrid(int[][] src) {
        int m = src.length;
        int[][] copy = new int[m][m];
        for (int i = 0; i < m; ++i) {
            System.arraycopy(src[i], 0, copy[i], 0, m);
        }
        return copy;
    }

    /**
     * 将卡片列表复制一份
     *
     * @param src 复制来源
     * @return 返回复制完成后的一份拷贝
     */
    public static List<Card> copyCards(List<Card> src) {
        List<Card> copy = new ArrayList<>();
        src.forEach(e -> {
            int row = e.getRow();
            int column = e.getColumn();
            int x = e.getX();
            int y = e.getY();
            int number = e.getNumber();
            Card copyCard = new Card(row, column, number);
            copyCard.setX(x);
            copyCard.setY(y);
            copy.add(copyCard);
        });
        return copy;
    }

    /**
     * 产生数字为2、4的随机卡片
     * <p>
     * 通过自定义的RandomList对象，在空白处随机选择生成卡片
     * <p>
     * 在游戏开始时，会生成两张卡片，之后每完成一次卡片移动，只随机产生一张。
     */
    public void randomCard() {
        int[][] grid = Board.getInstance().getGrid();
        int ROW_COUNT = Board.rows, COLUMN_COUNT = Board.cols;
        // collect blank positions
        List<Integer> blanks = new LinkedList<>();
        for (int i = 0; i < ROW_COUNT; ++i) {
            for (int j = 0; j < COLUMN_COUNT; ++j) {
                if (grid[i][j] == 0) {
                    blanks.add(i * COLUMN_COUNT + j);
                }
            }
        }
        RandomList randomList = new RandomList(blanks);
        boolean isEmpty = blanks.size() == Board.rows * Board.cols;
        ThreadManager threadManager = ThreadManager.getInstance();
        if (isEmpty) {
            threadManager.submit(new Thread(new DrawNewCard(newCard(randomList.getItem(), 2))));
            threadManager.submit(new Thread(new DrawNewCard(newCard(randomList.getItem(), 2))));
        } else {
            threadManager.submit(new Thread(new DrawNewCard(newCard(randomList.getItem(), randomNumber()))));
        }
        threadManager.waitFor(() -> MoveCardListener.getInstance().setAvailable(true));
    }


    /**
     * 初始化卡片，并将其添加到Board中，位置由参数决定
     * <p>
     *
     * @param index  在board中的位置，所谓的“在board中的位置”其实表示的是第几个格子，
     *               从左上角开始数，index为0，右下角为 15
     * @param number 显示在卡片上的数字
     * @return 返回完成初始化的Card对象
     */
    private Card newCard(int index, int number) {
        int[] p = Board.getPosition(index);
        int row = p[0], col = p[1];
        Card card = new Card(row, col, number);
        Board.getInstance().add(card);
        return card;
    }

    /**
     * 随机返回“2”，“4”中的一个数字，用于生成新卡片初始的数/
     *
     * @return 返回随机的“2”和“4”，但“4”的概率极低
     */
    private int randomNumber() {
        return (int) (Math.random() * 10) >= 8 ? 4 : 2;
    }

    /**
     * 用于创建能够返回任意一个当前处于“空白”的位置的随机集合对象
     * 每当返回一个位置，就会将这个位置删去，也就是说下一次不会随机到相同的位置
     */
    private static class RandomList {
        List<Integer> list;

        public RandomList(List<Integer> list) {
            this.list = list;
        }

        public int getItem() {
            int index = (int) (Math.random() * list.size());
            int ret = list.get(index);
            // 将末尾元素赋值到随机元素所在位置， 每次只移除末尾的元素，
            // 避免直接删除中间某个元素导致其后面元素的移动的开销
            // （虽然对于当前数量的来说开销很小）
            int lastIndex = list.size() - 1;
            list.set(index, list.get(lastIndex));
            list.remove(lastIndex);
            return ret;
        }
    }

    public void nextRound(int moveDirection, Map<Integer, Integer> moveMap, int newScore) {
        List<Runnable> drawTasks = getDrawTasks(moveDirection, moveMap);
        ThreadManager threadManager = ThreadManager.getInstance();
        for (Runnable r : drawTasks) {
            threadManager.submit(new Thread(r));
        }
        threadManager.waitFor(() -> {
            Board.getInstance().fixCards().setScore(newScore);
            randomCard();
            GameWindow.checkIfGameOver();
        });
    }

    private List<Runnable> getDrawTasks(int direction, Map<Integer, Integer> moveMap) {
        List<Card> list = Board.getInstance().getCards();
        List<Runnable> ret = new ArrayList<>();
        // 遍历所有卡片，将需要移动的卡片交给MovingCard对象，并用List集合存储
        for (Card card : list) {
            int pos = Board.queryPosition(card);
            if (moveMap.containsKey(pos)) {
                int next = moveMap.get(pos);
                int[] tuple = Board.getPosition(next);
                int nextRow = tuple[0], nextColumn = tuple[1];
                ret.add(new MovingCard(card, nextRow, nextColumn, direction));
            }
        }
        return ret;
    }

    public static CardsHandler getInstance() {
        return instance;
    }
}
